using System;
using System.Collections.Generic;
using foundation.structure;
using foundation.structure.link;
using System.Reflection;
namespace foundation.events
{
    public class MiniDispatcher : IEventDispatcher
    {
        internal Dictionary<string, Link<object>> EventListeners =
            new Dictionary<string, Link<object>>();

        internal IEventDispatcher Target;

        public MiniDispatcher(IEventDispatcher target = null)
        {
            if (target == null)
            {
                target = this;
            }

            Target = target;
        }

        public void AddEventListener(object type, Action<EventX> listener, int priority = 0)
        {
            this.doAddEventListener(type, listener, null, priority);
        }

        public void AddEventListener(object type, MethodInfo listener, object thisobj, int priority = 0)
        {
            var vo = this.doAddEventListener(type, listener, thisobj, priority);
        }



        private LinkVo<object> doAddEventListener(object type, object listener, object thisobj, int priority = 0)
        {
            // type = Convert.ChangeType(type, type.GetType());
            var typestr = Convert.ToString(type);
            Link<object> link;
            if (EventListeners.TryGetValue(typestr, out link) == false)
            {
                link = new Link<object>();
                EventListeners.Add(typestr, link);
            }

            var vo = null == thisobj ? link.Find(listener) : link.Find(listener, thisobj);
            if (null != vo)
            {
                if (vo.Weight == priority) return vo;
                vo.Clean();
            }

            vo = link.Push(listener, priority);
            vo.thisobj = thisobj;
            return vo;
        }

        public void RemoveEventListener(object type, Action<EventX> listener)
        {
            this.doRemoveEventListener(type, listener, null);
        }

        public void RemoveEventListener(object type, MethodInfo listener, object thisobj)
        {
            this.doRemoveEventListener(type, listener, thisobj);
        }

        private void doRemoveEventListener(object type, object listener, object thisobj)
        {
            var typestr = Convert.ToString(type);
            Link<object> link;
            if (!EventListeners.TryGetValue(typestr, out link)) return;
            if (null == thisobj)
            {
                link.Find(listener)?.Clean();
            }
            else
            {
                link.Find(listener, thisobj)?.Clean();
            }

        }

        public void DispatchEvent(EventX e)
        {
            var type = Convert.ToString(e.Type);
            Link<object> link;
            if (!EventListeners.TryGetValue(type, out link)) return;
            var param = new object[] { e };
            link.ForEach(vo =>
            {
                var handler = vo.Data;
                if (handler is MethodInfo)
                {
                    var info = (MethodInfo)handler;
                    info.Invoke(vo.thisobj, param);
                }
                else
                {
                    (handler as Action<EventX>)(e);
                }

                return !e.StopPropagation;
            });
        }


        public void SimpleDispatch(object type, object data = null, bool bubbles = false)
        {
            // Console.WriteLine("SimpleDispatch : " + type);
            var eventX = ClassUtils.PoolGet<EventX>();
            eventX.Target = eventX.CurrentTarget = Target;
            eventX.Type = type;
            eventX.Data = data;
            eventX.StopPropagation = false;
            eventX.Bubbles = bubbles;

            DispatchEvent(eventX);

            eventX.Dispose();
            ClassUtils.PoolSet(eventX);

        }
    }
}